package io.gitee.macxiang.sql.wapper;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;

import io.gitee.macxiang.annotation.Reader;
import io.gitee.macxiang.annotation.TableName;
import io.gitee.macxiang.entity.ExecNumber;
import io.gitee.macxiang.entity.SqlFieldMap;
import io.gitee.macxiang.sql.DatabaseService;
import io.gitee.macxiang.sql.SqlConfig;
import io.gitee.macxiang.utils.Datas;
import io.gitee.macxiang.utils.SQL;
import io.gitee.macxiang.utils.utils;
import jakarta.annotation.PostConstruct;

public class BaseService<T, W extends BaseWhere, S extends BaseSet> {
  final public Class<T> ce;
  final public Class<W> cw;
  final public Class<S> cs;
  final public W wp;
  final public List<SqlFieldMap> fs;
  final public String insertFormatter;
  final public SqlFieldMap primary;
  final public String updateFormatter;
  final public String sqlTableName;
  final public String selectField;
  final public String insertField;

  @Autowired
  public DatabaseService databaseService;
  @Autowired
  public SqlConfig sqlConfig;

  @SuppressWarnings("unchecked")
  public BaseService() {
    Type[] type = ((ParameterizedType) getClass().getGenericSuperclass())
        .getActualTypeArguments();
    this.ce = (Class<T>) type[0];
    this.cw = (Class<W>) type[1];
    W w = null;
    try {
      w = cw.getDeclaredConstructor().newInstance();
    } catch (Exception e) {
    }
    wp = w;

    this.cs = (Class<S>) type[2];
    HashMap<String, SqlFieldMap> tmp = new HashMap<>();
    this.fs = Reader.getSqlFieldMap(ce, tmp);
    this.primary = tmp.get("primary");
    List<String> t = new ArrayList<>(), f = new ArrayList<>();
    TableName tn = ce.getAnnotation(TableName.class);
    String tableName = tn.value();
    if (Datas.isEmpty(tableName)) {
      tableName = Datas.Hump2Underline(ce.getSimpleName());
    }
    if (tn.backtick() && tableName.indexOf('`') != 0) {
      tableName = String.format("`%s`", tableName);
    }
    for (SqlFieldMap e : fs) {
      if (e.exist) {
        if (e.select) {
          t.add(String.format("%s.%s", tableName, e.sqlName));
        }
        f.add(e.sqlName);
      }
    }
    selectField = String.join(", ", t);
    insertField = String.join(", ", f);
    insertFormatter = String.format("INSERT INTO %s (%s) VALUES\n  %%s;", tableName, insertField);
    updateFormatter = String.format("UPDATE %s SET %%s WHERE %s.%s = %%s", tableName, tableName, primary.sqlName);
    sqlTableName = tableName;
  }

  @PostConstruct
  public void init() {
    databaseService.initSqlFieldMap(fs, sqlTableName);
  }

  /**插入实体类数据
   * @param list  实体类数组
   * @return      {@link ExecNumber} 方便查看sql
   */
  public ExecNumber create(List<T> list) {
    List<String> values = new ArrayList<>();
    list.forEach(l -> {
      List<String> line = new ArrayList<>();
      fs.forEach(f -> {
        try {
          if (f.exist) {
            String v = f.getValue(l, true);
            line.add(v);
          }
        } catch (Exception e) {
        }
      });
      values.add(String.format("(%s)", String.join(", ", line)));
    });
    String sql = String.format(insertFormatter, String.join(",\n  ", values));
    return new ExecNumber(sql, p -> databaseService.save(sql));
  }

  /**插入实体类数据
   * @param t 单个实体类数据
   * @return  ExecNumber
   */
  public ExecNumber create(T t) {
    return create(new ArrayList<>(Arrays.asList(t)));
  }

  /**根据实体类的主键更新数据
   * @param entity  主键字段必须不为空的实体, 任意不为空的字段,以及注解配置自动update的字段,都将进行修改;
   * @return  {@link ExecNumber} 方便查看sql
   */
  public ExecNumber updateById(T entity) {
    String id = primary.getValue(entity, false);
    if (id != null) {
      List<String> s = new ArrayList<>();
      fs.forEach(e -> {
        if (e != primary) {
          String v = e.getValue(entity, false);
          if (v != null) {
            s.add(String.format("%s=%s", e.sqlName, v));
          }
        }
      });
      String sql = String.format(updateFormatter, String.join(", ", s), id);
      return new ExecNumber(sql, e -> databaseService.save(e));
    }
    return new ExecNumber(null, e -> 0);
    // return create(entity);
  }

  /**执行where语句
   * @param callback 回调函数
   * @return {@link WhereResult} 对象，包含生成的 SQL 查询
   */
  public WhereResult<T, W, S> where(java.util.function.Consumer<W> callback) {
    try {
      W w = cw.getDeclaredConstructor().newInstance();
      if (callback != null) {
        callback.accept(w);
      }
      return new WhereResult<>(w.getSql(sqlTableName), this);
    } catch (Exception e) {
      System.err.println("构造where错误");
      throw new RuntimeException(e);
    }
  }

  /**通过id获取实体
   * @param id  数据主键的值
   * @return    成功返回实体;失败返回null;
   */
  public T getById(String id) {
    return databaseService.selectOne(this.where(e -> {
      e.eq(primary.sqlName, utils.safeJSON(id), false);
    }).selectSql(false), ce, fs);
  }

  /**根据主键删除数据
   * @param ids 主键数组
   * @return    删除条目数
   */
  public int deleteById(List<String> ids) {
    List<String> a = new ArrayList<>();
    ids.forEach(id -> {
      try {
        id = SQL.safeJSON(id);
        if (!Datas.isEmpty(id)) {
          a.add(id);
        }
      } catch (Exception e) {
      }
    });
    System.out.println(String.format("DELETE FROM %s WHERE %s.%s IN (%s)",
        sqlTableName, sqlTableName, primary.sqlName, String.join(", ", a)));
    return 0;
  }

  /**根据主键删除数据
   * @param ids 主键数组
   * @return    删除条目数
   */
  public int deleteById(String[] ids) {
    return deleteById(Arrays.asList(ids));
  }

  /**根据主键删除数据
   * @param id  主键值
   * @return    删除条目数
   */
  public int deleteById(String id) {
    return deleteById(Arrays.asList(id));
  }

  /**删除数据; 没有自动添加limit数目限制,执行时注意安全;
   * @param where DELETE where后的sql语句;如果为空或者 true 则不执行;
   * @return      {@link ExecNumber} 方便查看sql
   */
  public ExecNumber deleteByWhere(String where) {
    if (!Datas.isEmpty(where)) {
      String sql = String.format("DELETE FROM %s WHERE %s",
          sqlTableName, where);
      System.out.println(sql);
    }
    return new ExecNumber(null, e -> 0);
  }

  /**执行sql
   * @param sql 语句
   * @return    如果是update语句或insert语句返回影响行数
   */
  public int runSql(String sql) {
    return databaseService.save(sql);
  }

  /**执行sql SELECT语句
   * @param sql SELECT语句
   * @return    List类型结果
   */
  public List<HashMap<String, Object>> selectSql(String sql) {
    return databaseService.selectList(sql);
  }

  /** 根据id查询数据是否存在
   * @param id  id
   * @return    存在返回true，不存在返回false
   */
  public boolean exists(String id) {
    return where(e -> e.eq(primary.sqlName, id, false)).count().exec() > 0;
  }

}